///////////////////////////////////////////////////////////////////////////////
//
// DBT86 Project : x86 Platform : x86 Interpreter : System instructions
// Copyright (C) 2004 by Ben Hertzberg and Stanford University
// All Rights Reserved
//
// This code may not be used for any purpose without the express written
// consent of the author.  See the LICENSE.txt file for details.
//
//
// DESCRIPTION
//
// This unit implements simulation of system operations on x86.
// It is also responsible for managing segmentation.
//
// TODO:
// - see TODO.txt for additional opcodes needing implementation
// - random further info in CPUID
// - make sure eventual CPUID return values are consistent with supported
//   functions
///////////////////////////////////////////////////////////////////////////////

#include "config.h"
#include "memops.h"
#include "interp.h"
#ifndef LESS86
#include "base/Simulator.h"
#endif

//
// shared CPUID macros
//
#define CPUID_FEATURE_FPU     0x00000001
#define CPUID_FEATURE_VME     0x00000002
#define CPUID_FEATURE_DE      0x00000004
#define CPUID_FEATURE_PSE     0x00000008
#define CPUID_FEATURE_TSC     0x00000010
#define CPUID_FEATURE_MSR     0x00000020
#define CPUID_FEATURE_PAE     0x00000040
#define CPUID_FEATURE_MCE     0x00000080
#define CPUID_FEATURE_CX8     0x00000100
#define CPUID_FEATURE_APIC    0x00000200
#define CPUID_FEATURE_RSVD0   0x00000400
#define CPUID_FEATURE_SEP     0x00000800
#define CPUID_FEATURE_SYSCALL 0x00000800
#define CPUID_FEATURE_MTRR    0x00001000
#define CPUID_FEATURE_PGE     0x00002000
#define CPUID_FEATURE_MCA     0x00004000
#define CPUID_FEATURE_CMOV    0x00008000
#define CPUID_FEATURE_PAT     0x00010000
#define CPUID_FEATURE_PSE_36  0x00020000
#define CPUID_FEATURE_PSN     0x00040000
#define CPUID_FEATURE_CLFLSH  0x00080000
#define CPUID_FEATURE_AMD_MP  0x00080000
#define CPUID_FEATURE_RSVD1   0x00100000
#define CPUID_FEATURE_DS      0x00200000
#define CPUID_FEATURE_ACPI    0x00400000
#define CPUID_FEATURE_MMX     0x00800000
#define CPUID_FEATURE_FXSR    0x01000000
#define CPUID_FEATURE_SSE     0x02000000
#define CPUID_FEATURE_SSE2    0x04000000
#define CPUID_FEATURE_SS      0x08000000
#define CPUID_FEATURE_HTT     0x10000000
#define CPUID_FEATURE_TM      0x20000000
#define CPUID_FEATURE_AMD_64  0x20000000
#define CPUID_FEATURE_IA_64   0x40000000
#define CPUID_FEATURE_AMD_3DX 0x40000000
#define CPUID_FEATURE_PBE     0x80000000
#define CPUID_FEATURE_AMD_3DN 0x80000000

#define CPUID_FEATURE_CFGS \
  ((CFG_MMX ? CPUID_FEATURE_MMX : 0) | \
   (CFG_SSE ? CPUID_FEATURE_SSE : 0) | \
   (CFG_SSE2 ? CPUID_FEATURE_SSE2 : 0))


//
// E1 CPUID settings
//
#if CFG_EMU_E1
#define CPUID_MAXINDEX 2
// string0..3 = "PureElektrik"
#define CPUID_ID0 "PureElektrik";
#define CPUID_ID1 "PureElektrik";
#define CPUID_ID2 "              eLekTRiK(tm) Virtuon(tm) Processor"
#define CPUID_VER_EXTFAMILY 0
#define CPUID_VER_EXTMODEL 0
#define CPUID_VER_TYPE 0
#define CPUID_VER_FAMILY 15
#define CPUID_VER_MODEL 15
#define CPUID_VER_STEPPING 0
#define CPUID_FEATURES_COMMON \
  (CPUID_FEATURE_CFGS | \
  CPUID_FEATURE_FPU | \
  CPUID_FEATURE_VME | \
  CPUID_FEATURE_PSE | \
  CPUID_FEATURE_TSC | \
  CPUID_FEATURE_MSR | \
  CPUID_FEATURE_PAE | \
  CPUID_FEATURE_CX8 | \
  CPUID_FEATURE_APIC | \
  CPUID_FEATURE_SEP | \
  CPUID_FEATURE_MTRR | \
  CPUID_FEATURE_PGE | \
  CPUID_FEATURE_CMOV | \
  CPUID_FEATURE_PAT | \
  CPUID_FEATURE_PSE_36 | \
  CPUID_FEATURE_CLFLSH | \
  CPUID_FEATURE_ACPI | \
  CPUID_FEATURE_FXSR | \
  CPUID_FEATURE_TM)
#define CPUID_FEATURES CPUID_FEATURES_COMMON

#define CPUID_EXT_MAXINDEX 0x80000004
#define CPUID_EXT_FEATURES ((CPUID_FEATURES_COMMON &~ CPUID_FEATURE_SEP) | CPUID_FEATURE_SYSCALL);

// TODO:  more stuff I'm sure... including feature descriptors
#else
#error TODO: CPUID for selected CFG_EMU is NYI
#endif

#ifndef CPUID_DESCR_0
#define CPUID_DESCR_0 0
#endif

#ifndef CPUID_DESCR_1
#define CPUID_DESCR_1 0
#endif

#ifndef CPUID_DESCR_2
#define CPUID_DESCR_2 0
#endif

#ifndef CPUID_DESCR_3
#define CPUID_DESCR_3 0
#endif

#ifndef CPUID_DESCR_4
#define CPUID_DESCR_4 0
#endif

#ifndef CPUID_DESCR_5
#define CPUID_DESCR_5 0
#endif

#ifndef CPUID_DESCR_6
#define CPUID_DESCR_6 0
#endif

#ifndef CPUID_DESCR_7
#define CPUID_DESCR_7 0
#endif

#ifndef CPUID_DESCR_8
#define CPUID_DESCR_8 0
#endif

#ifndef CPUID_DESCR_9
#define CPUID_DESCR_9 0
#endif

#ifndef CPUID_DESCR_10
#define CPUID_DESCR_10 0
#endif

#ifndef CPUID_DESCR_11
#define CPUID_DESCR_11 0
#endif

#ifndef CPUID_DESCR_12
#define CPUID_DESCR_12 0
#endif

#ifndef CPUID_DESCR_13
#define CPUID_DESCR_13 0
#endif

#ifndef CPUID_DESCR_14
#define CPUID_DESCR_14 0
#endif

static char CPUID_id0[12+1] = CPUID_ID0;
static char CPUID_id1[12+1] = CPUID_ID1;
#ifdef LESS86
static char CPUID_id2[48+1] = CPUID_ID2;
#endif

void CPUID() {
  IVAL idx, a,b,c,d;
  idx = LOAD_REG_EAX;
  switch(idx) {
    case 0:
      a = CPUID_MAXINDEX;
      b = CPUID_id0[0] | (CPUID_id0[1] << 8) | (CPUID_id0[2] << 16) | (CPUID_id0[3] << 24);
      d = CPUID_id0[4] | (CPUID_id0[5] << 8) | (CPUID_id0[6] << 16) | (CPUID_id0[7] << 24);
      c = CPUID_id0[8] | (CPUID_id0[9] << 8) | (CPUID_id0[10] << 16) | (CPUID_id0[11] << 24);
      break;
    case 1:
      a = (CPUID_VER_EXTFAMILY << 20) | (CPUID_VER_EXTMODEL << 16) | (CPUID_VER_TYPE << 12) | (CPUID_VER_FAMILY << 8) | (CPUID_VER_MODEL << 4) | (CPUID_VER_STEPPING);
      b = 0; // TODO: implement
      d = CPUID_FEATURES;
      c = 0; // TODO: implement
      break;
    case 2:
      a = 0x1 | (CPUID_DESCR_0 << 8) | (CPUID_DESCR_1 << 16) | (CPUID_DESCR_2 << 24);
      b = CPUID_DESCR_3 | (CPUID_DESCR_4 << 8) | (CPUID_DESCR_5 << 16) | (CPUID_DESCR_6 << 24);
      d = CPUID_DESCR_7 | (CPUID_DESCR_8 << 8) | (CPUID_DESCR_9 << 16) | (CPUID_DESCR_10 << 24);
      c = CPUID_DESCR_11 | (CPUID_DESCR_12 << 8) | (CPUID_DESCR_13 << 16) | (CPUID_DESCR_14 << 24);
      break;
    case 0x80000000:
      a = CPUID_EXT_MAXINDEX;
      b = CPUID_id1[0] | (CPUID_id1[1] << 8) | (CPUID_id1[2] << 16) | (CPUID_id1[3] << 24);
      d = CPUID_id1[4] | (CPUID_id1[5] << 8) | (CPUID_id1[6] << 16) | (CPUID_id1[7] << 24);
      c = CPUID_id1[8] | (CPUID_id1[9] << 8) | (CPUID_id1[10] << 16) | (CPUID_id1[11] << 24);
      break;
    case 0x80000001:
      a = (CPUID_VER_EXTFAMILY << 20) | (CPUID_VER_EXTMODEL << 16) | (CPUID_VER_TYPE << 12) | (CPUID_VER_FAMILY << 8) | (CPUID_VER_MODEL << 4) | (CPUID_VER_STEPPING);
      b = 0; // TODO: implement
      d = CPUID_EXT_FEATURES;
      c = 0; // TODO: implement
      break;
    // TODO: levels above 0x00000004
#ifdef LESS86
    case 0x80000002:
      a = CPUID_id2[0] | (CPUID_id2[1] << 8) | (CPUID_id2[2] << 16) | (CPUID_id2[3] << 24);
      b = CPUID_id2[4] | (CPUID_id2[5] << 8) | (CPUID_id2[6] << 16) | (CPUID_id2[7] << 24);
      c = CPUID_id2[8] | (CPUID_id2[9] << 8) | (CPUID_id2[10] << 16) | (CPUID_id2[11] << 24);
      d = CPUID_id2[12] | (CPUID_id2[13] << 8) | (CPUID_id2[14] << 16) | (CPUID_id2[15] << 24);
      break;
    case 0x80000003:
      a = CPUID_id2[16] | (CPUID_id2[17] << 8) | (CPUID_id2[18] << 16) | (CPUID_id2[19] << 24);
      b = CPUID_id2[20] | (CPUID_id2[21] << 8) | (CPUID_id2[22] << 16) | (CPUID_id2[23] << 24);
      c = CPUID_id2[24] | (CPUID_id2[25] << 8) | (CPUID_id2[26] << 16) | (CPUID_id2[27] << 24);
      d = CPUID_id2[28] | (CPUID_id2[29] << 8) | (CPUID_id2[30] << 16) | (CPUID_id2[31] << 24);
      break;
    case 0x80000004:
      a = CPUID_id2[32] | (CPUID_id2[33] << 8) | (CPUID_id2[34] << 16) | (CPUID_id2[35] << 24);
      b = CPUID_id2[36] | (CPUID_id2[37] << 8) | (CPUID_id2[38] << 16) | (CPUID_id2[39] << 24);
      c = CPUID_id2[40] | (CPUID_id2[41] << 8) | (CPUID_id2[42] << 16) | (CPUID_id2[43] << 24);
      d = CPUID_id2[44] | (CPUID_id2[45] << 8) | (CPUID_id2[46] << 16) | (CPUID_id2[47] << 24);
      break;
#endif
    // TODO: levels above 0x80000004
    default:
      a = 0;
      b = 0;
      d = 0;
      c = 0;
      break;
  }
  STORE_REG_ID_DWORD(EAX,a);
  STORE_REG_ID_DWORD(EBX,b);
  STORE_REG_ID_DWORD(ECX,c);
  STORE_REG_ID_DWORD(EDX,d);
}

// TODO: these ops
void INSB() { ASSERT(UNREACHED); }
void INSW() { ASSERT(UNREACHED); }
void INSD() { ASSERT(UNREACHED); }
void OUTSB() { ASSERT(UNREACHED); }
void OUTSW() { ASSERT(UNREACHED); }
void OUTSD() { ASSERT(UNREACHED); }
void IN_AL_Ib() { ASSERT(UNREACHED); }
void IN_AX_Ib() { ASSERT(UNREACHED); }
void IN_EAX_Ib() { ASSERT(UNREACHED); }
void OUT_Ib_AL() { ASSERT(UNREACHED); }
void OUT_Ib_AX() { ASSERT(UNREACHED); }
void OUT_Ib_EAX() { ASSERT(UNREACHED); }
void IN_AL_DX() { ASSERT(UNREACHED); }
void IN_AX_DX() { ASSERT(UNREACHED); }
void IN_EAX_DX() { ASSERT(UNREACHED); }
void OUT_DX_AL() { ASSERT(UNREACHED); }
void OUT_DX_AX() { ASSERT(UNREACHED); }
void OUT_DX_EAX() { ASSERT(UNREACHED); }

void INT_3() { ASSERT(UNREACHED); }
void INTO() { ASSERT(UNREACHED); }
void INT1() { ASSERT(UNREACHED); }
void HLT() { ASSERT(UNREACHED); }
void CLI() { ASSERT(UNREACHED); }
void STI() { ASSERT(UNREACHED); }

void LAR_Gw_Ew() { ASSERT(UNREACHED); }
void LAR_Gd_Ew() { ASSERT(UNREACHED); }
void LSL_Gw_Ew() { ASSERT(UNREACHED); }
void LSL_Gd_Ew() { ASSERT(UNREACHED); }
#if (!CFG_AMDEXT)
void LOADALL()  { ASSERT(UNREACHED); }
#else
void SYSRET() { ASSERT(UNREACHED); }
#endif
void CLTS()  { ASSERT(UNREACHED); }
void INVD()  { ASSERT(UNREACHED); }
void WBINVD() { ASSERT(UNREACHED); }
void MOV_Rd_Cd() { ASSERT(UNREACHED); }
void MOV_Cd_Rd() { ASSERT(UNREACHED); }
void MOV_Rd_Dd() { ASSERT(UNREACHED); }
void MOV_Dd_Rd() { ASSERT(UNREACHED); }
void MOV_Rd_Td() { ASSERT(UNREACHED); }
void MOV_Td_Rd() { ASSERT(UNREACHED); }
void WRMSR() { ASSERT(UNREACHED); }
void RDMSR() { ASSERT(UNREACHED); }

#ifndef LESS86 // ifndef seems backwards, but it isn't
void RDTSC() {
  unsigned long long x = global_sim->getSimclock();
  STORE_REG_ID_DWORD(EDX, (unsigned long)(x >> 32));
  STORE_REG_ID_DWORD(EAX, (unsigned long)x);
}
#else
void RDTSC() { ASSERT(UNREACHED); }
#endif

void RDPMC() { ASSERT(UNREACHED); }
void SYSEXIT() { ASSERT(UNREACHED); }
void RSM() { ASSERT(UNREACHED); }
void SLDT_Ew() { ASSERT(UNREACHED); }
void STR_Ew() { ASSERT(UNREACHED); }
void LLDT_Ew() { ASSERT(UNREACHED); }
void LTR_Ew() { ASSERT(UNREACHED); }
void VERR_Ew() { ASSERT(UNREACHED); }
void VERW_Ew() { ASSERT(UNREACHED); }
void SGDT_Ms()  { ASSERT(UNREACHED); }
void SIDT_Ms() { ASSERT(UNREACHED); }
void LGDT_Ms()  { ASSERT(UNREACHED); }
void LIDT_Ms()  { ASSERT(UNREACHED); }
void SMSW_Ew()  { ASSERT(UNREACHED); }
void LMSW_Ew()  { ASSERT(UNREACHED); }
void INVLPG_Mb()  { ASSERT(UNREACHED); }
void MONITOR() { ASSERT(UNREACHED); }

void CLFLUSH() { ASSERT(UNREACHED); }

#ifdef WITH_PIN_SUPPORT
void FXSAVE() {
    int i;
    
    STORE_VIRTUAL_WORD(X86_CTXT(FPcontrol),X86_TMP(va),X86_TMP(segOverride),0);
    STORE_VIRTUAL_WORD(X86_CTXT(FPstatus),X86_TMP(va) + 2,X86_TMP(segOverride),0);
    STORE_VIRTUAL_BYTE(X86_CTXT(FPtag),X86_TMP(va) + 4,X86_TMP(segOverride),0);
    STORE_VIRTUAL_WORD(0,X86_TMP(va) + 6,X86_TMP(segOverride),0);
    STORE_VIRTUAL_DWORD(0,X86_TMP(va) + 8,X86_TMP(segOverride),0);
    STORE_VIRTUAL_WORD(X86_CTXT(SegTable[CS].base),X86_TMP(va + 12),X86_TMP(segOverride),0);  
    STORE_VIRTUAL_DWORD(0,X86_TMP(va) + 16,X86_TMP(segOverride),0);    
    STORE_VIRTUAL_WORD(X86_CTXT(SegTable[DS].base),X86_TMP(va + 20),X86_TMP(segOverride),0);      
    STORE_VIRTUAL_DWORD(0,X86_TMP(va) + 24,X86_TMP(segOverride),0);    
    STORE_VIRTUAL_DWORD(0,X86_TMP(va) + 28,X86_TMP(segOverride),0);    

    // 8 fp gprs are recorded in the layout, neither more or less.
    // refer to http://support.intel.com/products/processor/manuals/index.htm.
    ASSERT(X86_NUM_FPR == 8);
    for(i=0;i<X86_NUM_FPR;i++) {
        STORE_VIRTUAL_UNALIGNED_QWORD(X86_CTXT(FPR[i].low),X86_TMP(va) + 16 * (i+2),X86_TMP(segOverride),0);    
        STORE_VIRTUAL_WORD(X86_CTXT(FPR[i].high),X86_TMP(va) + 16 * (i+2) + 8,X86_TMP(segOverride),0);    
    }
 
}

void FXRSTOR() {
    int i;

    FINIT();    
    //X86_CTXT(FPcontrol) = LOAD_VIRTUAL_WORD(X86_TMP(va),X86_TMP(segOverride),0);
    //X86_CTXT(FPstatus) = LOAD_VIRTUAL_WORD(X86_TMP(va) + 2,X86_TMP(segOverride),0);
    //X86_CTXT(FPtag) = LOAD_VIRTUAL_BYTE(X86_TMP(va) + 4,X86_TMP(segOverride),0);
    //LOAD_VIRTUAL_WORD(0,X86_TMP(va) + 6,X86_TMP(segOverride),0);
    //LOAD_VIRTUAL_DWORD(0,X86_TMP(va) + 8,X86_TMP(segOverride),0);
    X86_CTXT(SegTable[CS].base) = LOAD_VIRTUAL_WORD(X86_TMP(va) + 12,X86_TMP(segOverride),0);  
    //LOAD_VIRTUAL_DWORD(0,X86_TMP(va) + 16,X86_TMP(segOverride),0);    
    X86_CTXT(SegTable[DS].base) = LOAD_VIRTUAL_WORD(X86_TMP(va) + 20,X86_TMP(segOverride),0);      
    //LOAD_VIRTUAL_DWORD(0,X86_TMP(va) + 24,X86_TMP(segOverride),0);    
    //LOAD_VIRTUAL_DWORD(0,X86_TMP(va) + 28,X86_TMP(segOverride),0);    

    // 8 fp gprs are recorded in the layout, neither more or less.
    // refer to http://support.intel.com/products/processor/manuals/index.htm.
    ASSERT(X86_NUM_FPR == 8);
    for(i=0;i<X86_NUM_FPR;i++) {
        X86_CTXT(FPR[i].low) = LOAD_VIRTUAL_UNALIGNED_QWORD(X86_TMP(va) + 16 * (i+2),X86_TMP(segOverride),0);    
        X86_CTXT(FPR[i].high) = LOAD_VIRTUAL_WORD(X86_TMP(va) + 16 * (i+2) + 8,X86_TMP(segOverride),0);    
    }
 
}
#else
void FXSAVE() { ASSERT(UNREACHED); }
void FXRSTOR() { ASSERT(UNREACHED); }
#endif // WITH_PIN_SUPPORT

void LFENCE() { ASSERT(UNREACHED); }
void MFENCE() { ASSERT(UNREACHED); }
void SFENCE() { ASSERT(UNREACHED); }


#if !CFG_NATIVE_PROCESS
void INT_Ib() { ASSERT(UNREACHED); }
void SYSENTER() { ASSERT(UNREACHED); }
#if CFG_AMDEXT
void SYSCALL() { ASSERT(UNREACHED); }
#endif
#endif

// TODO: consider moving these to a separate unit that deals with memory and virtual memory

UINT8 LoadSegB(IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  return LoadB(va + X86_CTXT(SegTable[seg].base), uncached);
}

UINT16 LoadSegH(IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  return LoadH(va + X86_CTXT(SegTable[seg].base), uncached);
}

UINT32 LoadSegW(IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  return LoadW(va + X86_CTXT(SegTable[seg].base), uncached);
}

UINT64 LoadSegD(IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  return LoadD(va + X86_CTXT(SegTable[seg].base), uncached);
}

void StoreSegB(IVAL val, IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  StoreB(val, va + X86_CTXT(SegTable[seg].base), uncached);
}

void StoreSegH(IVAL val, IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  StoreH(val, va + X86_CTXT(SegTable[seg].base), uncached);
}

void StoreSegW(IVAL val, IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  StoreW(val, va + X86_CTXT(SegTable[seg].base), uncached);
}

void StoreSegD(IVAL val, IVAL va, UINT seg, bool uncached) {
  // TODO: limit check
  StoreD(val, va + X86_CTXT(SegTable[seg].base), uncached);
}

// TODO(mbarrien): This probably doesn't need to be global, but
// the PIC code below assumes it's global. Not going to fix it for now
volatile UINT32 segval;
void SetSegSelector(UINT id, UINT16 val) {
  // HACK: allow load of SS for proper operation on 64-bit
#if CFG_NATIVE_PROCESS && (defined(__GNUC__))
  if (id == SS) {
    segval = val;
#if __PIC__
    asm volatile(
       "call .LL20;\n"
        ".LL20:\n"
       "popl %%eax;\n"
       "addl $_GLOBAL_OFFSET_TABLE_+[.- .LL20],%%eax;\n"

       "movl segval@GOT(%%eax),%%eax;\n"
       "movl (%%eax),%%eax;\n"
       "mov %%eax, %%ss;\n"
       : : : "eax");
#else
    asm volatile(
       "movl segval, %%eax;\n"
       "mov %%eax, %%ss;\n"
       : : : "eax");
#endif
  } else {
#if CFG_NATIVE_PROCESS_FS_INTERCEPT
        if(id == FS) {
            // we allow only one FS that we support in native mode.
            ASSERT(X86_CTXT(fsSegSelector) == 0);
            X86_CTXT(fsSegSelector) = val;
            return;
        }
#endif
#if CFG_NATIVE_PROCESS_GS_INTERCEPT
        if(id == GS) {
            // we allow only one GS that we support in native mode.
            ASSERT(X86_CTXT(gsSegSelector) == 0);
            X86_CTXT(gsSegSelector) = val;
            return;
        }
#endif

    ASSERT(UNREACHED);
  }
#else
  ASSERT(UNREACHED);
#endif
}

UINT16 GetSegSelector(UINT id) {
    UINT16 segval = 0;
    switch(id) {
        case ES:
            asm volatile(
               "mov %%es, %0;\n"
               :"=r"(segval): :);
            break;
        case CS:  
            asm volatile(
               "mov %%cs, %0;\n"
               :"=r"(segval): :);
            break;
        case SS:
            asm volatile(
               "mov %%ss, %0;\n"
               :"=r"(segval): :);
            break;
        case DS: 
            asm volatile(
               "mov %%ds, %0;\n"
               :"=r"(segval): :);
            break;     
        case FS:
#if CFG_NATIVE_PROCESS_FS_INTERCEPT
            if(X86_CTXT(fsSegSelector) != 0) {
                segval = X86_CTXT(fsSegSelector);
            } else
#endif
            {
                asm volatile(
                   "mov %%fs, %0;\n"
                   :"=r"(segval): :);
            }
            break;    
        case GS:
#if CFG_NATIVE_PROCESS_FS_INTERCEPT
            if(X86_CTXT(gsSegSelector) != 0) {
                segval = X86_CTXT(gsSegSelector);
            } else
#endif
            {
                asm volatile(
                   "mov %%gs, %0;\n"
                   :"=r"(segval): :);
            }
            break; 
        default:
            ASSERT(UNREACHED);
    }
    return segval;
}
